#!/usr/bin/perl
#
#	Author:  Brandon Shields (brandon.d.shields a@t gee.mail dot com)
#	Based on source by: Steve Adeff from ragetvgrab
#		and the guys at The Shepherd (http://svn.whuffy.com)
#
#	Project lives at: http://code.google.com/p/mythvideo-scanner/
#
#	To install missing modules:
#	cpan <module>
#	cpan Text::Levenshtein
#
#	DATA DICTIONARY
#
#	hash details		{'Title'}
#						{'Subtitle'}
#						{'Season'}
#						{'Episode'}
#						{'Prematch'}
#						{'Postmatch'}
#						{'PosterUrl'}
#						{'ScreenshotUrl'}
#						{'BannerUrl'}
#						{'FanartUrl'}
#						{'PosterFile'}
#						{'ScreenshotFile'}
#						{'BannerFile'}
#						{'FanartFile'}
#						{'Length'}
#						{'Director'}
#						{'Year'}
#						{'Cast'}
#						{'Plot'}
#						{'Rating'}
#						{'status'}
#  bit mask for status
#		0x10	Season info
#		0x08	Episode info
#		0x02	Title
#		0x01	Subtitle
#

BEGIN
{
	#Add some search locations to the path
	$command = $0;
#	$CWD = $ENV{PWD};
	$command =~ s/\/[^\/]*$//;
	push(@INC,"$command");
	push(@INC,"/usr/local/share/mythvideo-scanner/");
	push(@INC,"/usr/share/mythvideo-scanner/");
}


use HTML::Parser;
use HTML::TreeBuilder;
use HTML::Entities;
use LWP::Simple;
use LWP::UserAgent;
use Getopt::Long;
#use Cwd 'fast_abs_path';
use File::Spec;
use Text::Levenshtein qw(distance);


#this library has been modified to return all hits form a DB query
use modules::MythTV;
use grabbers::imdb;
use grabbers::tvrage;
use grabbers::ttvdb;
use grabbers::tmdb;
use grabbers::cinecurry;
use grabbers::ttvdb_old;


#Add list of grabber plugins here
my @plugins=(
	grabbers::ttvdb->new(),
	grabbers::tmdb->new(),
	grabbers::ttvdb_old->new(),
	grabbers::imdb->new(),
	grabbers::tvrage->new(),
	grabbers::cinecurry->new(),
);

my $help='';
my $justnew='';
my $update='';
my $redo='';
my $getposter='';
my $dryrun='';
my $nolookup='';

my $version='3.3';
my $_rev = '$Rev$';
my $cover_path='';
my @cover_group;
my $banner_path='';
my @banner_group;
my $fanart_path='';
my @fanart_group;
my $screenshot_path='';
my @screenshot_group;

my @video_group;
my @video_folders;
my %cmdline_file_hints;



my $status='';
my $verbose='';

#Process options
GetOptions (
	'help' => \$help,
	'justnew' => \$justnew,
	'update' => \$update,
	'reparse' => \$reparse,
	'getposter' => \$getposter,
	'dryrun' => \$dryrun,
	'nolookup' => \$nolookup,
	'status' => \$status,
	'verbose' => \$verbose,
        'hint=s' => \%cmdline_file_hints,
);

if($help == 1)
{
	$_rev =~ s/\$//g;
	print "mythvideo-scanner\n";
	print "http://code.google.com/p/mythvideo-scanner/\n";
	print "Version: $version, $_rev\n";
	print "\n";
	print "Usage: mythvideo-scanner <options> <path>\n";
	print "\n";
	print "<path>       Optional path to scan. \n";
	print "                EG.   \\myth\\video\n";
	print "                      \\mnt\\share\:\\mnt\\sda\n";
	print "Options:\n";
	print "--hint <key>='<value>'   Manual intervention of parsing\n";
	print "                         valid keys:\n";
	print "                           Title, Season, Episode, Year\n";
	print "--justnew				Only parse files not found in db\n";
	print "--update                 Search for meta data for all videos found.\n";
	print "--reparse                Re-parse filename for details.\n";
	print "--getposter              Attempt to retrieve a poster.\n";
	print "--nolookup               Don't conduct internet searches\n";
	print "--dryrun                 Don't update database.\n";
	print "\n";
	print "--status                 Status of grabbers.\n";
	print "--verbose                Prints information about the parsing process.\n";
	print "\n";
	exit 0;
}

if($status)
{
	$_rev =~ s/\$//g;
	print "mythvideo-scanner\n";
	print "http://code.google.com/p/mythvideo-scanner/\n";
	print "Version: $version, $_rev\n";
	print "\n";
	foreach $grabber (@plugins)
	{
		next if $grabber == 0;
		print "Grabber: ".$grabber->title().", Version: ".$grabber->version()."\n";
	}
	exit 0;
}


#don't decend beyond these directory names.
my @blacklist = ('sample','subs','VIDEO_TS','proof');

# Scene markers that help find info in the filename
my $markers;
$markers->{scene} = [
		"PROPER","SCREENER","KAMERA","INTERNAL","LiNE","iAPULA","LIMITED","DIMENSION","DIAMOND","inf","iNFAMOUS"
		];
$markers->{frame}=[
		"BRRip","BDRip","BluRay","DVDRip","DVD-Rip","WEBRip","Pdvd Rip","anivcd","TELESYNC","WS","TS",'480p','720p','1080i','1080p',"HDTV","UNCUT","REPACK","CD1","CD2","CD3","PAL","NTSC",
		];
$markers->{codec}=[
		"AC3","XVID","DIVX",'x264',
		];
$markers->{region}=[
		"R5","R1","R2",
		];

#get host name
my $hostname = `/bin/hostname`;
chomp($hostname);
print "Using settings for ". $hostname ."\n";;

#get DB schema version
my $db_schema;
$db_schema = getSetting("mythvideo.DBSchemaVer");
if( ! $db_schema){
	$db_schema = getSetting("DBSchemaVer");
}
print "Mythvideo Database schema is ".$db_schema.", suggests Mythtv version " ;
if( $db_schema < 1028 ) {
	print "<= 0.21\n";
}
elsif( $db_schema < 1032) {
	print "==0.22\n";
}
elsif( $db_schema < 1037) {
	print "==0.23\n";
}
elsif( $db_schema == 1037 ) {
	print "==0.24\n";
}
elsif( $db_schema < 1317 ) {
	print "== 0.25...0.26\n";
}
elsif( $db_schema == 1317 ) {
	print "== 0.27\n";
}
elsif( $db_schema > 1317 ) {
	print ">= 0.28\n";
}

# Build our list of locations to search for videos
#   Command line option
@video_folders = @ARGV;
if(@video_folders == 1 )
{
	@video_folders = split(/:|;/, $video_folders[0]);
}
#   Storage group
@video_group = getStorageGroup("Videos");
#   All else fails, get it from settings table
if(@video_folders == 0)
{
	my $path='';
	$path = getSetting("VideoStartupDir");
	@video_folders = split(/:|;/, $path);
	# Added storage group to search list, skip repeats
	foreach my $item (@video_group)
	{
		#remove tailing slash
		$item =~ s/\/?$//;
		if(@video_folders > 0)	{
			foreach my $test (@video_folders)
			{
				#remove tailing slash
				$test =~ s/\/?$//;
				if($item eq $test)	{
					last;
				}
			}
			continue
			{
				push(@video_folders, $item);
			}
		}
		else{
			push(@video_folders, $item);
		}
	}
}

#get path to posters on current host
@cover_group = getStorageGroup("Coverart");
@banner_group = getStorageGroup("Banners");
@fanart_group = getStorageGroup("Fanart");
@screenshot_group = getStorageGroup("Screenshots");
if(@cover_group){
	$cover_path = $cover_group[0];
}
else{
	$cover_path = getSetting("VideoArtworkDir");
}
$cover_path =~ s/\/?$//;
if( $db_schema >= 1028 )
{
	if(@banner_group){
		$banner_path = $banner_group[0];
	}
	else{
		$banner_path = getSetting("mythvideo.bannerDir");
	}
	$banner_path =~ s/\/?$//;

	if(@fanart_group){
		$fanart_path = $fanart_group[0];
	}
	else{
		$fanart_path = getSetting("mythvideo.fanartDir");
	}
	$fanart_path =~ s/\/?$//;

	if(@screenshot_group){
		$screenshot_path = $screenshot_group[0];
	}
	else{
		$screenshot_path = getSetting("mythvideo.screenshotDir");
	}
	$screenshot_path =~ s/\/?$//;
	print "Images will be downloaded to:\n";
	print " Covers:      '".$cover_path."'\n";
	print " ScreenShots: '".$screenshot_path."'\n";
	print " Fanart:      '".$fanart_path."'\n";
	print " Banners:     '".$banner_path."'\n";
	
}
else
{
	print "Covers will be downloaded to: '" .$cover_path."'\n";
}

#get supported extensions, start off with a default set.
my @filetypes = ('avi','mpg','mpeg','mkv');
my $ext = MythTV::query("SELECT extension FROM videotypes WHERE f_ignore = '0'");
if(@$ext>0)
{
	foreach my $extension (@$ext)
	{
		push(@filetypes,$extension->[0]);
	}
}

#build the ratings to parental level mapping
my %rating_2_pl_map;

for ($i=1; $i < 5; $i++)
{
	$rating_string = getSetting("mythvideo.AutoR2PL".$i);
	@ratings = split(/:|;/, $rating_string);
	foreach $rating (@ratings)
	{
		$rating_2_pl_map{$rating}=$i;
	}
}
if($verbose)
{
	print "Will automatically map Rating as such:\n";
	foreach $mapping (keys %rating_2_pl_map)
	{
		print $mapping."\t->\t".$rating_2_pl_map{$mapping}."\n";
	}
	print "unknown\t->\t".GetParentalLevel("unknown")."\n";
	
}

sub GetParentalLevel
{
	my $rating=shift;
	my $pl=4;
	if(exists $rating_2_pl_map{$rating})
	{
		$pl = $rating_2_pl_map{$rating};
	}
	return $pl;
}

print "Seaching for these extensions: "."@filetypes"."\n";

# Start searching.
foreach $directory(@video_folders)
{
	$directory = File::Spec->rel2abs($directory);
	#remove tailing slash
	$directory =~ s/\/?$//;
	print "Starting search in '" .$directory."'\n";
	process_branch($directory);
}

sub process_branch
{
	my @files;
	my $path;
	my $skip;	
	$path=shift;
	if( -d "$path")
	{
		opendir(hDir, $path) || return;
		@files=readdir(hDir);
		closedir(hDir);
	}
	else
	{
		#remove leading slash
		$path =~ s/^\/?//;
		push(@files,$path);
		$path = '';
	}
	foreach $file (@files)
	{
		#ignore . and .. and 'dot files' special folders
		next if $file =~ /^\./;

		#skip file if it is blacklisted
		$skip=0;
		foreach my $ignore (@blacklist)
		{
			if ("$file" =~ /^$ignore$/i)
			{
				$skip=1;
				last;
			}
		}
		next if $skip == 1;

		#check for directory
		if( -d "$path/$file")
		{
			process_branch("$path/$file");
		}
		else
		{
			#only process files we know
			if(valid_ext("$path/$file"))
			{
				print "\n\x1b\x5b0;31m$path/$file" . "\x1b\x5b0m\n";
				process_file("$path/$file", \@files);
			}
		}
	}
}

sub valid_ext
{
	my $filename = shift;
	my $result = 0;
	foreach my $extension (@filetypes)
	{
		if ($filename =~ /$extension$/i)
		{
			$result = 1;
			last;
		}
	}
	return $result;
}

sub process_file
{
	my $full_filename = shift;
	my $rFiles = shift;
	my %file_details;
	my %parsing_details;

	#MySQL queries don't like single quotes, so lets delimit them
	my $qFilename = $full_filename;
	$qFilename =~ s/\'/\\\'/g;

	my $min_q = 12;
	# get existing data from database
	%parsing_details = getDetailsFromDb($full_filename);
	if( length $parsing_details{Title} > 0)
	{
		if( $justnew == 1 )
		{
			return;
		}
	}
	if($verbose == 1)
	{
		print "Existing Data...\r\n";
		printDetails(\%parsing_details);
		printBrief(\%parsing_details);
	}
	if($db_schema >= 1028)
	{
		$min_q = 17;
	}
	else
	{
		$min_q = 12;
	}
	# check if we need to proceed or not
	if(!$update)
	{
		if(scoreDetails(\%parsing_details) >= $min_q)
		{
			printBrief(\%parsing_details);
			print " Existing details are satisfactory.\n";
			print " Use --update to override.\n";
			return;
		}
	}
	if($parsing_details{Inetref}==99999999)
	{
		print " Inetref set to 99999999.\n";
		return;
	}

	# Check if we should use the existing data for queries
	if( $reparse==1 )
	{
		if($verbose == 1)
		{
			print "Reparsing details from scratch...\r\n";
		}
		undef %parsing_details;
	}
	if(!gotDetails(\%parsing_details))
	{
		# look for an imdb ref in a nfo file
		$ref = findnfo($full_filename);
		if( $nolookup == 0)
		{
			if(length $ref > 0)
			{
				$imdb_grabber = 	grabbers::imdb->new();
				%parsing_details = $imdb_grabber->search_by_ref($ref);
				if($verbose == 1)
				{
					print "After Ref lookup...\r\n";
					printDetails(\%parsing_details);
				}
			}
		}
		$parsing_details{'filename'} = $full_filename;
		if(!gotDetails(\%parsing_details))
		{
			# look for a numbering patern with other files
			findNumberPatern(\%parsing_details, $rFiles);
			if($verbose == 1)
			{
				print "After numbering patern match...\r\n";
				printDetails(\%parsing_details);
			}
			if(!gotDetails(\%parsing_details))
			{
				#look for episode indicators
				find_episode(\%parsing_details);
				if($verbose == 1)
				{
					print "After naming syntax match...\r\n";
					printDetails(\%parsing_details);
				}
			}
			if(!gotDetails(\%parsing_details))
			{
				#look for a title
				if( length $parsing_details{Title} == 0)
				{
					$parsing_details{Title} = find_title($full_filename, $parsing_details{Prematch});
					if($verbose == 1)
					{
						print "After title search...\r\n";
						printDetails(\%parsing_details);
					}
				}
			}
			if(length $parsing_details{Year} != 4 )
			{
				$parsing_details{Year} = find_year($full_filename);
			}
			if(length $ref == 0)
			{
				cleanDetails(\%parsing_details);
			}
		}
	}
	print("Checking cmdline hints...\r\n");
	%tmp = %cmdline_file_hints;
	mergeDetails(\%tmp, \%parsing_details);
	%parsing_details = %tmp;
	printDetails(\%parsing_details);
	#lookup the data
	if( $nolookup==0 )
	{
		foreach $grabber (@plugins)
		{
			next if $grabber == 0;
			print "Grabber: ".$grabber->title().", Version: ".$grabber->version()."\n";
			my %new_details = $grabber->search($parsing_details{Title},$parsing_details{Subtitle},$parsing_details{Season},$parsing_details{Episode},$parsing_details{Year});
			mergeDetails(\%file_details, \%new_details);
			printBrief(\%file_details);
			if(scoreDetails(\%file_details) >= 15)
			{
				last;
			}
		}
	}
	if($getposter)
	{
		my $get_response;
		if($db_schema < 1028)
		{
			#check for urls to images
			if(length $file_details{PosterUrl} > 0 )
			{
				$titleimage = $file_details{PosterUrl};
			}
			else
			{
				$titleimage = "";
			}
			if(length $file_details{ScreenshotUrl} > 0 )
			{
				$episodeimage = $file_details{ScreenshotUrl};
			}
			else
			{
				$episodeimage = $titleimage;
			}
			if(length $titleimage > 0 )
			{
				#get the episode image
				$poster_filename = $full_filename.".jpg";
				$poster_filename =~ s/.*\///g;
				$poster_filename = $cover_path."/".$poster_filename;
	
				$folder_filename = $full_filename;
				$folder_filename =~ s/\/[^\/]*$/\/folder.jpg/;
				
				$get_response = getImage($episodeimage, $poster_filename);
				if( $get_response >= 0 )
				{
					# we got something, or something already exists
					$file_details{PosterFile} = $poster_filename;
				}
				
				#get the title image
				$get_response = getImage($titleimage, $folder_filename);
				if( $get_response == 0 )
				{
					# we got something, or something already exists
					$folder2_filename = $full_filename;
					$folder2_filename =~ s/\/[^\/]*\/[^\/]*$/\/folder.jpg/;
					if(-l "$folder2_filename")
					{
						print "Removing old symbolic link: ".$folder2_filename."\n";
						unlink($folder2_filename);
					}
					if(!(-r "$folder2_filename"))
					{
						print "Creating symbolic link: ".$folder2_filename."\n";
						symlink($folder_filename, $folder2_filename);
					}
				}
			}
			else
			{
				print "Image not available.\n";
			}
		}
		else
		{
			my $image_filename;
			if(length $file_details{PosterUrl} >0 )
			{
				if( length $file_details{PosterFile} > 0)
				{
					$image_filename = $file_details{PosterFile};
				}
				else
				{
					$image_filename = $file_details{Title};
					if(length $file_details{Season} > 0){
						$image_filename .= "_season_".$file_details{Season};
					}
					else
					{
						if(length $file_details{Subtitle} > 0){
							$image_filename .= ": ".$file_details{Subtitle};
						}
					}
					$image_filename .="_poster.jpg";
					$image_filename =~ s/.*\///g;
					$image_filename = $cover_path."/".$image_filename;
				}
				$get_response = getImage($file_details{PosterUrl}, $image_filename);
				if( $get_response >= 0 )
				{
					# we got something, or something already exists
					$file_details{PosterFile} = $image_filename;
				}
			}
			if(length $file_details{ScreenshotUrl} >0 )
			{
				if( length $file_details{ScreenshotFile} > 0)
				{
					$image_filename = $file_details{ScreenshotFile};
				}
				else
				{
					$image_filename = "$file_details{Title}_S$file_details{Season}E$file_details{Episode}_srnshot.jpg";
					$image_filename =~ s/.*\///g;
					$image_filename = $screenshot_path."/".$image_filename;
				}
				$get_response = getImage($file_details{ScreenshotUrl}, $image_filename);
				if( $get_response >= 0 )
				{
					# we got something, or something already exists
					$file_details{ScreenshotFile} = $image_filename;
				}
			}
			if(length $file_details{BannerUrl} >0 )
			{
				if( length $file_details{BannerFile} > 0)
				{
					$image_filename = $file_details{BannerFile};
				}
				else
				{
					$image_filename = $file_details{Title};
					if(length $file_details{Season} > 0){
						$image_filename .= "_season_".$file_details{Season};
					}
					else
					{
						if(length $file_details{Subtitle} > 0){
							$image_filename .= ": ".$file_details{Subtitle};
						}
					}
					$image_filename .="_banner.jpg";
					$image_filename =~ s/.*\///g;
					$image_filename = $banner_path."/".$image_filename;
				}
				$get_response = getImage($file_details{BannerUrl}, $image_filename);
				if( $get_response >= 0 )
				{
					# we got something, or something already exists
					$file_details{BannerFile} = $image_filename;
				}
			}
			if(length $file_details{FanartUrl} >0 )
			{
				if( length $file_details{FanartFile} > 0)
				{
					$image_filename = $file_details{FanartFile};
				}
				else
				{
					$image_filename = $file_details{Title};
					if(length $file_details{Season} > 0){
						$image_filename .= "_season_".$file_details{Season};
					}
					else
					{
						if(length $file_details{Subtitle} > 0){
							$image_filename .= ": ".$file_details{Subtitle};
						}
					}
					$image_filename .="_fanart.jpg";
					$image_filename =~ s/.*\///g;
					$image_filename = $fanart_path."/".$image_filename;
				}
				$get_response = getImage($file_details{FanartUrl}, $image_filename);
				if( $get_response >= 0 )
				{
					# we got something, or something already exists
					$file_details{FanartFile} = $image_filename;
				}
			}
		}
	}
	mergeDetails(\%file_details, \%parsing_details);
	if($file_details{Episode} > 0 && length $file_details{Subtitle} == 0 && length $file_details{Postmatch} > 0)
	{
		$file_details{Subtitle} = $file_details{Postmatch};
	}
	$file_details{Title} = clean_html($file_details{Title});
	printDetails(\%file_details);
	UpdateDataBase($full_filename,\%file_details);
}

# look for existance of a file that contains a substring in its name
sub findfile
{
	my $string = shift;
	my $filename = shift;
	my $files = shift;
	my $return = 0;
	my $origscore;
	my $score=-500;
	my $thisscore;
	($path, $basename, $ext) = $filename =~ m/(.*?\/)([^\/]+?)(($)|(\.[^\.]{1,4}$))/;	
#	print $filename."\n";
#	print $path.":".$basename.":".$ext."\n";
	$origscore = comparestrings($string,$basename);
#	print "Target: ".$origscore."\n";
	#look for similar files
	foreach $file (@{$files})
	{
		if(valid_ext($file))
		{
			($path, $basename, $ext) = $file =~ m/(.*?\/|^)([^\/]+?)($|\.[^\.]{1,4}$)/;	
			$thisscore = comparestrings($string,$basename);
#			print $basename.": ".$thisscore."\n";
			if($thisscore > $score)
			{
				$score = $thisscore;
			}
		}
	}
	if($score > $origscore)
	{
		$return = 1;
	}	
	return $return;
}

# Look for an nfo file with similar name, and return imdb reference
sub findnfo
{
	my $fullfilename = shift;
	my $path = $fullfilename;
	my $file = "";
	my $video_filename = "";
	my $loops=3;
	my $filename="";
	my $ref="";
	my @list;
	while( $loops > 0 )
	{
		($path, $file, $ext) = $path =~ m/(.*?)\/([^\/]+?)(($)|(\.[^\.]{1,4}))$/;	
		if(length $video_filename == 0)
		{
			$video_filename = $file;
		}
		opendir(hpath, $path) || return;
		@list=readdir(hpath);
		closedir(hpath);
		foreach $entry (@list)
		{
			if($entry =~ /\.nfo$/)
			{
				my $tmp;
				$tmp = $entry;
				$tmp =~ s/\.nfo$//;
				if(comparestrings($tmp, $video_filename) >= 80)
				{
					$filename = "$path/$entry";
					print "Found NFO file".$filename."\n";
					$loops = 0;
					last;
				}
			}
		}
		$loops = $loops-1;
		$prev = $file;
	}
	if(length $filename > 0)
	{
		open (NFOFILE, $filename);
		while ($line = <NFOFILE>)
		{
			($ref)=$line =~ m/tt(\d{7})/;
			if (length $ref > 0)
			{
				print " Found imdb ref: ".$ref."\n";
				last;
			}
		}
		close(NFOFILE);
	}
	return $ref;
}

sub comparestrings
{
	my $first = shift;
	my $second = shift;
	my $likeness;
	# make the comparison a little easier by removing
	# qutoes commas etc, plus make both string upper case.
	# Convert single digits to words
	$first = digit2str($first);	
	$second = digit2str($second);	
	$first = uc($first);
	$second = uc($second);
	$first =~ s/[^\w\s]//g;
	$second =~ s/[^\w\s]//g;
	$leven_distance = distance($first, $second);
	my $totallen = length($first) + length($second);
	return (($totallen - $leven_distance) / $totallen) * 100;
}

sub digit2str
{
	my $phrase = shift;
	$phrase =~ s/\D1\D/one/g; 
	$phrase =~ s/\D2\D/two/g; 
	$phrase =~ s/\D3\D/three/g; 
	$phrase =~ s/\D4\D/four/g; 
	$phrase =~ s/\D5\D/five/g;
	$phrase =~ s/\D6\D/six/g; 
	$phrase =~ s/\D7\D/seven/g; 
	$phrase =~ s/\D8\D/eight/g; 
	$phrase =~ s/\D9\D/nine/g; 
	$phrase =~ s/\D10\D/ten/g; 
	return $phrase;
}

# removes all markers from candidate string
# converts some characters to space"
sub clean_title
{
	my $title = shift;
	my $tmp;
	$title = cleanMarkers($title);
	#strip text within ()
	$title =~ s/\(.*\)//g;
	#strip quotes
	$title =~ s/[\'\"]//g;
	#strip some characters
	$title =~ s/[\.\-\_\(\)\[\]]/ /g;
	#strip resolution marker  WWWWxHHHH
	$title =~ s/\d?\d\d\dx\d?\d\d\d//i;
	$title = cleanNonWords($title);
	$title = cleanUpperGarbage($title);
	$title = cleanWhiteSpace($title);
	return $title;
}

sub clean_prematch
{
	my $title = shift;
	#if if it like this 'bob1/bob2' make it 'bob2'
	$title =~ s/.*?([^\/]*)\/?$/\1/;
	#strip text within ()
	$title =~ s/\(.*\)//g;
	#strip quotes
	$title =~ s/[\'\"]//g;
	#strip some characters
	$title =~ s/[\.\-\_\(\)\[\]]/ /g;
	#strip resolution marker  WWWWxHHHH
	$title =~ s/\d?\d\d\dx\d?\d\d\d//i;
	$title = cleanMarkers($title);
	$title = cleanNonWords($title);
	$title = cleanUpperGarbage($title);
	$title = cleanWhiteSpace($title);
	return $title;
}

# removes all markers from candidate string
# converts some characters to space"
sub clean_postmatch
{
	my $title = shift;
	my $tmp;
	#if if it like this 'bob1/bob2' make it 'bob2'
	$title =~ s/^.*\///g;
	#strip quotes
	$title =~ s/[\'\"]//g;
	#strip some characters
	$title =~ s/[\.\-\_\(\)\[\]]/ /g;
	#strip resolution marker  WWWWxHHHH
	$title =~ s/\d?\d\d\dx\d?\d\d\d//i;
	$title = cleanMarkers($title);
	$title = cleanNonWords($title);
	$title = cleanUpperGarbage($title);
    # remove digits from post match
    $title =~ s/\d//g;
	$title = cleanWhiteSpace($title);

	return $title;
}

sub cleanUpperGarbage
{
	my $title = shift;
	#final clean up is if title like this "The Quick Brown Fox NEW SCENE"
	#then remove the uppercase part
	$tmp = $title;
	if( $title =~ /[a-z]{3,}/ )
	{
		$title =~ s/[A-Z]*[a-z0-9]?[A-Z]+[a-z0-9]?[A-Z]+[a-z0-9]?[A-Z]*//g;
		#strip leading whitespace
		$title =~ s/^\s*//;
		#strip trailing whitespace
		$title =~ s/\s*$//;
		#strip run of whitespace
		$title =~ s/\s\s+/ /g;
		#if it goes horribley wrong, revert back
		if( length $title == 0)
		{
			$title = $tmp;
		}
	}
	return $title;
}

sub cleanWhiteSpace
{
	my $title = shift;
	#remove white spaces from various parts
	#strip leading whitespace
	$title =~ s/^\s*//;
	#strip trailing whitespace
	$title =~ s/\s*$//;
	#strip runs of whitespace
	$title =~ s/\s\s+/ /g;
	return $title;
}

sub cleanNonWords
{
	my $title = shift;
	#remove words that contain no vowels
	$tmp = "";

	#remove words with no vowels
	while($tmp ne $title)
	{
		$tmp = $title;
		$title =~ s/(^|[^A-Z0-9])[BCDFGHJKLMNPQRSTVWXZ]{3,}([^A-Z0-9]|$)//ig;
	}
	return $title;
}

sub cleanMarkers
{
	my $title = shift;
	foreach my $type (keys %{($markers)})
	{
		foreach my $marker (@{($markers->{$type})})
		{
			$title =~ s/\W$marker[^\/]*//ig;
		}
	}
	return $title;
}

sub clean_nonutf8
{
	my $data = shift;
	$data =~ s/\x{e2}\x{80}\x{99}/\'/g;
	$data =~ s/\x{e2}\x{80}\x{98}/\'/g;
	$data =~ s/\x{e2}\x{80}\x{9C}/\"/g;
	$data =~ s/\x{e2}\x{80}\x{9D}/\"/g;
	return $data;
}

sub clean_html
{
	my $data = shift;
	$data =~ s/&amp\;/&/g;
	return $data;
}

sub find_title
{
	my $filename = shift;
	my $prematch = shift;
	my $title;
	my $tmp;
	$title="";

	#find candidate for title by looking for scene markers
	foreach my $type (keys %{($markers)})
	{
		foreach my $marker (@{($markers->{$type})})
		{
			#look for text before a marker
			($tmp) = $filename =~ m/\/([^\/]+)\W$marker\W/i;
			$tmp = clean_title($tmp);
			if( length $tmp > length $title)
			{
				$title = $tmp;
			}
		}
	}
	print "Title so far: ".$title."\n";
	#compare found title to what may exist before episode patern
	#if the prematch is "/path/to/season title/" we want "season title"
	print "Prematch: ".$prematch."\n";
	$tmp = clean_prematch($prematch);
	print "Cleaned prematch: ".$tmp."\n";
	if(length $title == 0)
	{
		$title = $tmp;
	}
	elsif( length $tmp > length $title)
	{
		#check for sub string 
		if( $tmp !~ /$title/i)
		{
#			print "Title: ".$title." tmp: ".$tmp."\n";
			$title = $tmp;
		}
	}
	elsif( length $tmp < length $title)
	{
		#check for sub string 
		if( $title =~ /$tmp/i)
		{
#			print "Title: ".$title." tmp: ".$tmp."\n";
			$title = $tmp;
		}
	}
	#Check path of file and if a directory level is a substring of title
	#   so far, then choose that as our title
	if( length $title > 0)
	{
		my $path;
		$path = $filename;
		while ( length $path > 0)
		{
			# strip trailing slash
			$path =~ s/\/$//;
			# break off last level in path
			($path, $level) = $path =~ m/(.*?)([^\/]*)$/i;
			next if ($level =~ /\.\./);
			if(length $level > 0)
			{
				if( $title =~ /(^|\W)$level(\W|$)/i)
				{
					print "Found better title in path: ".$level."\n";
					$title = $level;
					last;
				}
			}
		}
	}

	#default title is the filename without extention
	if( length $title == 0 )
	{
		($tmp) = $filename =~ m/.*\/(.*)\..*$/;
		$title = clean_title($tmp);
	}
	#remove the production year if present
	$title =~ s/(.{2,})\d\d\d\d$/\1/;
	return $title;
}

sub find_year()
{
	my $filename = shift;
	my $year = 0;
	my $done = 0;

	$input = $filename;
	while(!$done)
	{
		@date = gmtime(time());
		($tmp) = $input =~ m/.*(\d{4}).*?$/;
		if(($tmp > 1950) && ($tmp <= (1900+$date[5])) )
		{
			$year = $tmp;
			$done = 1;
		}
		else
		{
			# remove that test case
			$input =~ s/(.*)\d{4}.*?$/\1/;
		}
		if( length $tmp == 0)
		{
			# couldn't find anything
			$done = 1;
		}
	}
	return $year;
}

# Attempts to identify the season and episode of a file
# Requires there to be several examples of the same
# numbering method in the current directory.
#	findNumberPatern(\$parsing_details, $rFiles);

sub findNumberPatern()
{
	my $parsing_details = shift;
	my $files = shift;

	my $path;
	my $filename;
	my $extension;
	my @searchComponents;

	my @titlefields;
	my $title = "";
	my $subtitle = "";

	my @numberValues=([],[],[],[],[],[],[],[],[]);
	my $episodeIndex=-1;
	my $episodeCount=0;
	my $seasonIndex=-1;
	my $seasonCount=0;

	($path, $filename, $extension) = $parsing_details->{'filename'} =~ m/(.*)\/(.*)\.(.*)$/;
	#break filename up into string/number segments
	$filename =~ m/(\D*)(\d*)(\D*)(\d*)(\D*)(\d*)(\D*)(\d*)(\D*)/;
	@searchComponents = ($1,$2,$3,$4,$5,$6,$7,$8,$9);
	
	#look for similar files
	foreach $file (@{$files})
	{
		# restrict search to file with same extension
		if(!valid_ext("$file"))
		{
			next;
		}
		# break test case up into string/number components
		$file =~ m/(\D*)(\d*)(\D*)(\d*)(\D*)(\d*)(\D*)(\d*)(\D*)/;
		@components = ($1,$2,$3,$4,$5,$6,$7,$8,$9);
		for($index=0; $index<9; $index = $index+1)
		{
			if(length $searchComponents[$index])
			{
				if( ($index%2)==0 )
				{
					# Index is for string element
					# Title fields are consistant across files
					if(uc($components[$index]) eq uc($searchComponents[$index]))
					{
						$titlefields[$index] = $titlefields[$index] + 1;
					}
					else
					{
						last;
					}
				}
				else
				{
					# Index is for number element
					if( length $components[$index])
					{
						push(@{$numberValues[$index]}, int($components[$index]));
					}
					else
					{
						last;
					}
				}
			}
			elsif(length $components[$index])
			{
				# Length must be the same
				last;
			}
		}
	}
	# Analise Number field
	# Look for episode number
	for($index=0; $index < @numberValues; $index = $index+1)
	{
		@sorted = sort isgreater @{$numberValues[$index]};
		$size = @sorted;
		$range = $sorted[-1] - $sorted[0];
		# Episode number is where:
		#  range of values covers 75% of the number of values
		#  best coverage of number range
		if( $range > ($size * 0.75))
		{
			if($episodeCount < $size)
			{
				$episodeIndex = $index;
				$episodeCount = $size;
			}
		}
	}
	# look for season number, quantity must match episodes, and stop when at episode field
	for($index=0; $index < @numberValues; $index = $index+1)
	{
		if($index == $episodeIndex)
		{
			last;
		}
		@{$numberValues[$index]} = sort(isgreater @{$numberValues[$index]});
		$size = @{$numberValues[$index]};
		if(($size > $seasonCount) && ($size > ($episodeCount * 0.75)))
		{
			$seasonIndex = $index;
			$seasonCount = $size;
		}
	}
	if($episodeCount > 5)
	{
		$postmatchIndex=-1;
		$prematchIndex=-1;
		if($seasonCount == 0)
		{
			# look for mixed season and episode number field
			#   Must have more than 9 files to deduce info
			$epifieldLength = length findSeasonSize(@numberValues[$episodeIndex]);
			$totalfieldLength = length $numberValues[$episodeIndex][-1];
			if($totalfieldLength > $epifieldLength)
			{
				$parsing_details->{Season}=int($searchComponents[$episodeIndex]/(10**$epifieldLength));
				$parsing_details->{status}|=0x10;
			}
			$parsing_details->{Episode}=int($searchComponents[$episodeIndex]%(10**$epifieldLength));
			$parsing_details->{status}|=0x08;
			$postmatchIndex=$episodeIndex+1;
			$prematchIndex=$episodeIndex-1;
		}
		else
		{
			if($seasonIndex>-1)
			{
				$parsing_details->{Season}=int($searchComponents[$seasonIndex]);
				$parsing_details->{status}|=0x10;
				$postmatchIndex=$seasonIndex+1;
				$prematchIndex=$seasonIndex-1;
			}
			else
			{
			}
			if($episodeIndex>-1)
			{
				$parsing_details->{Episode}=int($searchComponents[$episodeIndex]);
				$parsing_details->{status}|=0x08;
				if($postmatchIndex<$episodeIndex)
				{
					$postmatchIndex = $episodeIndex+1;
				}
				if($prematchIndex>$episodeIndex)
				{
					$prematchIndex = $episodeIndex-1;
				}
			}
			else
			{
			}
		}
		if($episodeIndex)
		{
			
		}
		#get postmatch
		if($postmatchIndex>-1)
		{
			for($index=$postmatchIndex; $index < @searchComponents; $index =$index+1)
			{
				$subtitle = $subtitle." ".$searchComponents[$index];
			}
			if(length $subtitle)
			{
				$parsing_details->{Postmatch}=$subtitle;
			}
		}
		# Get prematch (parts of filename that are constant across files)
		for($index=0; $index <= $prematchIndex; $index = $index+1)
		{
			if($titlefields[$index] > 1)
			{
				$title = $title." ".$searchComponents[$index];
			}
		}
		$parsing_details->{Prematch} = $path."/".$title;
	}
}

# Work out the size of the season (number of episodes in season)
# this should tell us if we have:
# Production number,
# SEE,
# SSEE.
sub findSeasonSize
{
	my $array = shift;
	my $seasonSize=0;
	my $lastValue;
	my $step;
	$lastValue = $$array[0];
	foreach $value (@{$array})
	{
		# check for different century
		if( int($value/100) != int($lastValue/100) )
		{
			# allow for skipped episodes (max season size is 70 episodes)
			if( ($lastValue%100) < 70)
			{
				# season size is difference between point
				# of discontinuity and start of array
				# (rounded down to nearest century).
				$seasonSize = $lastValue-(abs($$array[0]/100)*100);
				last;
			}
		}
		$lastValue = $value;
	}
	# failed to find discontinuity
	if($seasonSize == 0)
	{
		$seasonSize = $$array[-1]%100;
	}
	# Min season size is 10 episodes, but more
	# importanly, it is represented by no less
	# than two digits
	if($seasonSize<10)
	{
		$seasonSize=10;
	}
	return ($seasonSize);
}

sub isgreater
{
	my $aa = $a;
	my $bb = $b;
	my $result =0;
	if($a > $b)
	{
		$result = 1;
	}
	elsif($a < $b)
	{
		$result = -1;
	}
	return ($result)
}

# This routine tries to find season and episode information
# Valid season and episode data is entered into the file_details
# Struct.
# Pre and post match data is kept.  The shortest Pre-match
# string will be retained for title search later.
#"
sub find_episode
{
	my $parsing_details = shift;
	my $filename = $parsing_details->{filename};

	# remove extension
	$filename =~ s/\.....?$//;
	if(length $parsing_details->{Prematch} == 0)
	{
		$parsing_details->{Prematch}=$filename;
	}
	if(length $parsing_details->{Postmatch} == 0)
	{
		$parsing_details->{Postmatch}=$filename
	}

	#first, lets match the obvious, These should have a Title element, if so, use it
	#match "blah/title SssEee subtitle"
	if($filename =~ /^(.*\/)([^\/]*)S(\d\d?)E(\d\d?)(.*)$/i)
	{
		if( length $parsing_details->{Prematch} > length $1)
		{
			$parsing_details->{Prematch}=$1;
		}
		if( length $parsing_details->{Title} == 0)
		{
			$parsing_details->{Title} = $2;
			$parsing_details->{status}|=0x01;
		}
		else
		{
			if( comparestrings($parsing_details->{Title},$2) > 30 )
			{
				$parsing_details->{Title} = clean_title($2);
			}
		}
		$parsing_details->{Season}=$3;
		$parsing_details->{Episode}=$4;
		if( length $parsing_details->{Postmatch} > length $5)
		{
			$parsing_details->{Postmatch}=$5;
		}
		$parsing_details->{status}|=0x18;
		if($verbose == 1)
		{
			print "Found SssEee...\r\n";
			printDetails($parsing_details);
		}
	}
	#match "blahssEeeblah"
	elsif($filename =~ /^(.*\/)([^\/]*)(\d\d?)E(\d\d?)(.*)$/i)
	{
		if( length $parsing_details->{Prematch} > length $1)
		{
			$parsing_details->{Prematch}=$1;
		}
		if( length $parsing_details->{Title} == 0)
		{
			$parsing_details->{Title} = $2;
		}
		$parsing_details->{Season}=$3;
		$parsing_details->{Episode}=$4;
		if( length $parsing_details->{Postmatch} > length $5)
		{
			$parsing_details->{Postmatch}=$5;
		}
		$parsing_details->{status}|=0x18;
		if($verbose == 1)
		{
			print "Found ssEee...\r\n";
			printDetails($parsing_details);
		}
	}
	#match "blahssXeeblah"
	elsif($filename =~ /^(.*\/)([^\/]*?)(\d?\d)X(\d\d?)(.*)$/i)
	{
		$Prematch = $1;
		$Title = $2;
		$Season = $3;
		$episode = $4;
		$postmatch = $5;
#		print $Prematch."\|".$Title."\|".$Season."\|".$episode."\|".$postmatch."\n";
		if( ((length $Title == 0) || ($Title=~/[^\d]$/)) && ((length $postmatch ==0) || ($postmatch=~/^[^\d]/))  )
		{
#			print "Found ssXee match\n";
			if( length $parsing_details->{Prematch} > length $Prematch)
			{
				$parsing_details->{Prematch}=$Prematch;
			}
			if( length $parsing_details->{Title} == 0)
			{
				$parsing_details->{Title} = $Title;
			}
			$parsing_details->{Season}=$Season;
			$parsing_details->{Episode}=$episode;
			if( length $parsing_details->{Postmatch} > length $postmatch)
			{
				$parsing_details->{Postmatch}=$postmatch;
			}
			$parsing_details->{status}|=0x18;

			if($verbose == 1)
			{
				print "Found ssXee...\r\n";
				printDetails($parsing_details);
			}
		}
	}
	#match "blahSss Eeeblah"
	elsif($filename =~ /(.*)S(\d?\d)\sE(\d\d)(\D.*)/)
	{
		if( length $parsing_details->{Prematch} > length $1)
		{
			$parsing_details->{Prematch}=$1;
		}
		$parsing_details->{Season}=$2;
		$parsing_details->{Episode}=$3;
		if( length $parsing_details->{Postmatch} > length $4)
		{
			$parsing_details->{Postmatch}=$4;
		}
		$parsing_details->{status}|=0x18;
		if($verbose == 1)
		{
			print "Found Sss Eee...\r\n";
			printDetails($parsing_details);
		}

	}

	#Look for other types of markers
	#match "Season ss"
	if($filename =~ /(.*)Season\D?(\d?\d)(\D.*)/i)
	{
		if( (length $parsing_details->{Prematch} == 0) || (length $parsing_details->{Prematch} > length $1))
		{
			$parsing_details->{Prematch}=$1;
		}
		if( !( $parsing_details->{status} & 0x10 ) )  #if we don't have Season info, look for it
		{
			$parsing_details->{Season}=$2;
		}
		if( length $parsing_details->{Postmatch} > length $3)
		{
			$parsing_details->{Postmatch}=$3;
		}
		$parsing_details->{status}|=0x10;
		if($verbose == 1)
		{
			print "Found SEASONss...\r\n";
			printDetails($parsing_details);
		}

	}
	#match "blah SEssblah"
	elsif($filename =~ /(.*)[\/\.\-\_\s]SE?\D?(\d?\d)(.*)/i)
	{
		if( length $parsing_details->{Prematch} > length $1)
		{
			$parsing_details->{Prematch}=$1;
		}
		if( !( $parsing_details->{status} & 0x10 ) )  #if we don't have Season info, look for it
		{
			$parsing_details->{Season}=$2;
		}
		if( length $parsing_details->{Postmatch} > length $3)
		{
			$parsing_details->{Postmatch}=$3;
		}
		$parsing_details->{status}|=0x10;
		if($verbose == 1)
		{
			print "Found SEss...\r\n";
			printDetails($parsing_details);
		}

	}


	#Look for episode markers
	#match "blahEPeeblah"
	if($filename =~ /(.*)[\W_]EP?\D?(\d?\d?\d)((\W.*)|$)/i)
	{
		if( length $parsing_details->{Prematch} > length $1)
		{
			$parsing_details->{Prematch}=$1;
		}
		if( !( $parsing_details->{status} & 0x08 ) )  #if we don't have Season info, look for it
		{
			$parsing_details->{Episode}=$2;
		}
		if( length $parsing_details->{Postmatch} > length $3)
		{
			$parsing_details->{Postmatch}=$3;
		}
		$parsing_details->{status}|=0x08;
		if($verbose == 1)
		{
			print "Found EPee...\r\n";
			printDetails($parsing_details);
		}

	}
	#match "blahEPISODE eeblah"
	elsif($filename =~ /(.*)EPISODE\D?(\d?\d?\d)(\D.*)/i)
	{
		if( length $parsing_details->{Prematch} > length $1)
		{
			$parsing_details->{Prematch}=$1;
		}
		if( !( $parsing_details->{status} & 0x08 ) )  #if we don't have Season info, look for it
		{
			$parsing_details->{Episode}=$2;
		}
		if( length $parsing_details->{Postmatch} > length $3)
		{
			$parsing_details->{Postmatch}=$3;
		}
		$parsing_details->{status}|=0x08;
		if($verbose == 1)
		{
			print "Found EPISODEee...\r\n";
			printDetails($parsing_details);
		}
	}

	# Usually this is of the form "Series Title SEE Episode Title.ext", so scrutise the length of those two parts.
	if($filename =~ /^(.*\/)([^\/]*)(\d)(\d\d)(\D.*|$)/)
	{
		$Prematch = $1;
		$Title = $2;
		$Season = $3;
		$episode = $4;
		$postmatch = $5;
		if( ((length $Title == 0) || ($Title =~ /.*\W$/)) && ((length $postmatch ==0) || ($postmatch =~ /^\W.*/)))
		{
			if( !( $parsing_details->{status} & 0x10 ) )  #if we don't have Season info, look for it
			{
				$parsing_details->{Season}=$Season;
			}
			else
			{	# if we do have season info, do some sanity checking
				if($parsing_details->{Season} != $Season)
				{
					goto end_see;
				}
			}
			if( !( $parsing_details->{status} & 0x08 ) )  #if we don't have Season info, look for it
			{
				$parsing_details->{Episode}=$episode;
			}
			else
			{	# if we do have episode info, do some sanity checking
				if($parsing_details->{Episode} != $episode)
				{
					goto end_see;
				}
			}
			if( (length $parsing_details->{Prematch}==0) || (length $parsing_details->{Prematch} > length $Prematch))
			{
				$parsing_details->{Prematch}=$Prematch;
			}
			if( length $parsing_details->{Title} == 0)
			{
				$parsing_details->{Title} = $Title;
			}
			if( length $parsing_details->{Postmatch} > length $postmatch)
			{
				$parsing_details->{Postmatch}=$postmatch;
			}
			$parsing_details->{status}|=0x18;
			if($verbose == 1)
			{
				print "Found SEE...\r\n";
				printDetails($parsing_details);
			}
		}
	}
end_see:
	#match "blahPARTeeblah"
	if($filename =~ /(.*)(PT)\D?(\d?\d)(\D?.*)/i)
	{
		if( !( $parsing_details->{status} & 0x08 ) )  #if we don't have episode info, look for it
		{
			$parsing_details->{Episode}=$3;
			if( length $parsing_details->{Prematch} > length $1)
			{
				$parsing_details->{Prematch}=$1;
			}
			if( length $parsing_details->{Postmatch} == $lenpostmatch)
			{
				$parsing_details->{Postmatch}=$4;
			}
			$parsing_details->{status}|=0x08;
			if($verbose == 1)
			{
				print "Found PARTee...\r\n";
				printDetails($parsing_details);
			}
		}
	}
	#match "blaheeofEEblah"
	elsif($filename =~ /(.*)(\d?\d)\W?of\W?\d?\d(.*)/i)
	{
		if( !( $parsing_details->{status} & 0x08 ) )  #if we don't have episode info, look for it
		{
			$parsing_details->{Episode}=$2;
			if( length $parsing_details->{Postmatch} > length $3)
			{
				$parsing_details->{Postmatch}=$3;
			}
			# Strip out a potential "Pt" before the eeofEE match
			$tmp = $1;
			$tmp =~ s/Pt$//i;
			if( length $parsing_details->{Prematch} > length $tmp)
			{
				$parsing_details->{Prematch}=$tmp;
			}
			$parsing_details->{status}|=0x08;
			if($verbose == 1)
			{
				print "Found eeOFEE...\r\n";
				printDetails($parsing_details);
			}
		}
	}


	if( ($parsing_details->{status}&0x18) == 0 )
	{
#		$parsing_details->{Prematch}="";
	}
	if(( $parsing_details->{status} & 0x08 ) && ( !( $parsing_details->{status} & 0x10 )))
	{
		#if we have episode, but not Season, assume Season 1
		#$parsing_details->{Season}=1;
	}
	if( length $parsing_details->{Postmatch} == length $filename)
	{
		$parsing_details->{Postmatch}="";
	}
	#strip leading zeroes
	$parsing_details->{Season} =~ s/^0//;
	$parsing_details->{Episode}=~ s/^0//;
	$parsing_details->{Title} =~ s/\./ /g;
}

# This routine conducts a search in Google, then looks for 
# "Did you mean" which is generally an error corrected
# version of the search.

sub GoogleErrorCorrect
{
	my $string = shift;
	my $searchstring = $string;
	$searchstring =~ s/\ /\+/g;	

	print "Before spell check: ".$string."\n";

	my $ua = new LWP::UserAgent(agent => 'Firefox');
	my $HTML = $ua->request(HTTP::Request->new(GET => "http://www.google.com/search?hl=en&q=\"$searchstring\"&num=0"));
	my $Parser = new HTML::TreeBuilder;
	my $Doc = $Parser->parse($HTML->content);
	$Doc->objectify_text();
	my @TextData = $Doc->look_down("_tag","~text");

	my $stage=0;
	my $result = "";
	foreach my $section (@TextData)
	{
		if($stage == 0)
		{
			if($section->attr_get_i('text') eq "Did you mean: ")
			{
				$stage =1;
			}
		}
		elsif($stage == 1)
		{
			if($section->attr_get_i('text') ne "No standard web pages containing all your search terms were found. ")
			{
				$text = $section->attr_get_i('text');
				$result = $result.$text;
			}
			else
			{
				$result =~ s/[^a-zA-Z0-9\s]//g;
				$stage = 2;
			}
		}
	}
	if (length $result == 0 )
	{
		$result = $string;
	}
	print "After spell check: ".$result."\n";
	return $result;
}

sub gotDetails
{
	my $ref =shift;
	my %file_details = %$ref;
	my $result=0;
	if(length $file_details{Title})
	{
		$result|=0x01;
	}
	if(length $file_details{Season})
	{
		$result|=0x02;
	}
	if(length $file_details{Episode})
	{
		$result|=0x04;
	}
	if(length $file_details{Subtitle})
	{
		$result|=0x08;
	}
	return $result==0x07;
}

# any details that the lower quality grabber has which
#  the higher quality grabber does not, then copy it over
sub mergeDetails
{
	my $highref = shift;
	my $lowref = shift;

	# step through each lowDetail, copy across
	#  if does not exist in highDetail
	foreach my $type (keys %$lowref)
	{
		if( length $highref->{$type} == 0 && length $lowref->{$type} > 0 )
		{
			$highref->{$type} = $lowref->{$type};
		}
	}
}

sub scoreDetails
{
	my $ref = shift;
	my %Details=%$ref;

	my $result=0;

	foreach my $type (keys %Details)
	{
		if($type eq "Plot")
		{
			if( length $Details{Plot} > 20 )
			{
				$result = $result + 5;
			}
		}
		elsif($type =~  /(Url|File)$/)
		{
			if(length $Details{$type} > 0)
			{
				$result = $result + 5;
			}
		}
		elsif($type eq "Season")
		{
			if( $Details{Season} > 0)
			{
				$result = $result + 1;
			}
		}
		elsif($type eq "Episode")
		{
			if( $Details{Episode} > 0)
			{
				$result = $result + 1;
			}
		}
		elsif($type eq "Length")
		{
			if( $Details{Length} > 0 )
			{
				$result = $result + 1;
			}
		}
		elsif( length $Details{$type} > 0 )
		{
			# add one point for each additional detail, whatever it may be.
			$result = $result + 1;
		}
	}
	return $result;
}

sub cleanDetails
{
	my $file_details = shift;

	$file_details->{Title} = clean_title($file_details->{Title});
	#Check path of file and if a directory level is a substring of title
	#   so far, then choose that as our title
	my $path;
	$path = $file_details->{filename};
	while ( length $path > 0)
	{
		# strip trailing slash
		$path =~ s/\/$//;
		# break off last level in path
		($path, $level) = $path =~ m/(.*?)([^\/]*)$/i;
		if(length $level > 0)
		{
			next if ($level =~ /\.\./);
			if( $file_details->{Title} =~ /(\W|^)$level(\W|$)/i)
			{
				print "Found better title in path: ".$level."\n";
				$file_details->{Title} = $level;
				$file_details->{Title} = clean_title($file_details->{Title});
				last;
			}
		}
	}


	$file_details->{Prematch} = clean_prematch($file_details->{Prematch});
	# remove title from postmatch
	$file_details->{Postmatch} =~ s/$file_details->{Title}//i;
	$file_details->{Postmatch} = clean_postmatch($file_details->{Postmatch});
}

sub printBrief
{
	my $ref = shift;
	print " ";
	foreach my $type (keys %$ref)
	{
		if( length $ref->{$type} )
		{
			print $type.", ";
		}
	}
	print "\n";
	print "------------------------------\n";
}

sub printDetails
{
	my $ref = shift;
	my %file_details = %$ref;
	print "Details ------------------------------\n";
	if(length $file_details{Title})
	{
		print "Title: ".$file_details{Title}."\n";
	}
	if(length $file_details{Season})
	{
		print " Season: ".$file_details{Season}."\n";
	}
	if(length $file_details{Episode})
	{
		print " Episode: ".$file_details{Episode}."\n";
	}
	if(length $file_details{Subtitle})
	{
		print " Subtitle: ".$file_details{Subtitle}."\n";
	}
	if(length $file_details{Prematch})
	{
		print " Prematch: ".$file_details{Prematch}."\n";
	}
	if(length $file_details{Postmatch})
	{
		print " Postmatch: ".$file_details{Postmatch}."\n";
	}
	if(length $file_details{PosterUrl})
	{
		print " Poster Image: ".$file_details{PosterUrl}."\n";
	}
	if(length $file_details{PosterFile})
	{
		print " Poster Image: ".$file_details{PosterFile}."\n";
	}
	if(length $file_details{ScreenshotUrl})
	{
		print " Screenshot Image: ".$file_details{ScreenshotUrl}."\n";
	}
	if(length $file_details{ScreenshotFile})
	{
		print " Screenshot Image: ".$file_details{ScreenshotFile}."\n";
	}
	if(length $file_details{BannerUrl})
	{
		print " Banner Image: ".$file_details{BannerUrl}."\n";
	}
	if(length $file_details{BannerFile})
	{
		print " Banner Image: ".$file_details{BannerFile}."\n";
	}
	if(length $file_details{FanartUrl})
	{
		print " Fanart Image: ".$file_details{FanartUrl}."\n";
	}
	if(length $file_details{FanartFile})
	{
		print " Fanart Image: ".$file_details{FanartFile}."\n";
	}
	if(length $file_details{Length})
	{
		print " Length: ".$file_details{Length}." minutes\n";
	}
	if(length $file_details{Director})
	{
		print " Director: ".$file_details{Director}."\n";
	}
	if(length $file_details{Cast})
	{
		print " Cast: ".$file_details{Cast}."\n";
	}
	if(length $file_details{Year} && $file_details{Year} > 1900)
	{
		print " Year: ".$file_details{Year}."\n";
	}
	if(length $file_details{Plot})
	{
		print " Plot: ".$file_details{Plot}."\n\n";
	}
	print "End ------------------------------\n";
}

sub getSetting
{
	my $data='';
	my $result;
	my $value = shift;
	$result = MythTV::query("SELECT data FROM settings WHERE value = '$value' AND ( hostname = '$hostname' or hostname is null )");
	if(@$result>0)
	{
		$data = $result->[0]->[0];
	}
	else
	{
		print "Setting not found: $value\n";
	}
	return $data;
}

sub getStorageGroup
{
	my $groupname = shift;
	my @group;
	#   Storage group
	if( $db_schema >= 1028 )
	{
		$dir = MythTV::query("SELECT dirname FROM storagegroup WHERE groupname = '$groupname' AND hostname = '$hostname'");
		if(@$dir>0)
		{
			foreach my $item (@$dir)
			{
				my $tmp;
				#remove tailing slash
				$tmp = $item->[0];
				$tmp =~ s/\/?$//;
				push(@group,$tmp);
			}
		}
		else
		{
			print "Storagegroup \'$groupname\' not defined\n";
		}
	}
	return @group;
}

sub getGroupSubpath
{
	my $filename = shift;
	my $ref = shift;
	my @group = @$ref;
	#  Set the default return path to complete filename
	my $subpath = $filename;
	foreach my $group_dir (@group)
	{
		if(length $group_dir > 0 )
		{
			if( $filename =~ /^$group_dir\/(.*)/ )
			{
				$subpath = $1;
#				print "Found item in video group: $group_dir\n";
#				print "                           $subpath\n";
				last
			}
		}
	}
	return $subpath;
}

sub getDetailsFromDb
{
	my $filename = shift;

	my %result;
	if( $db_schema >= 1028 )
	{
		my $migrated=0;
		my $rel_filename = getGroupSubpath($filename,\@video_group);
		my $qrel_filename = $rel_filename;
		$qrel_filename =~ s/\'/\\\'/g;
		my $db_data = MythTV::query("SELECT title,subtitle,director,plot,year,length,season,episode,coverfile,screenshot,banner,fanart,inetref,intid,filename,rating FROM videometadata WHERE filename LIKE '%$qrel_filename'");
		if(@$db_data)
		{
			$result{Title} = $db_data->[0]->[0];
			$result{Subtitle} = $db_data->[0]->[1];
			$result{Director} = $db_data->[0]->[2];
			$result{Plot} = $db_data->[0]->[3];
			$result{Year} = $db_data->[0]->[4];
			if( $db_data->[0]->[5] > 0 )
			{
				$result{Length} = $db_data->[0]->[5];
			}
			$result{Season} = $db_data->[0]->[6];
			$result{Episode} = $db_data->[0]->[7];
			$result{PosterFile} = $db_data->[0]->[8];
			$result{ScreenshotFile} = $db_data->[0]->[9];
			$result{BannerFile} = $db_data->[0]->[10];
			$result{FanartFile} = $db_data->[0]->[11];
			$result{Inetref} = $db_data->[0]->[12];
			$result{Rating} = $db_data->[0]->[15];
			# Migrate Title if season, episode info found
			if($result{Title} =~ /^(.*?)\s+S(\d\d?)E(\d\d?)\s*(.*?)$/i)
			{
				$migrated=1;
				$result{Title} = $1;
				$result{Season} = $2;
				$result{Episode} = $3;
				$result{Subtitle} = $4;
			}
			if( $db_data->[0]->[14] ne $rel_filename )
			{
				my $query;
				# migrate filename to use storage group
				print "Migrating metadata to storage group\n";
				$query = "UPDATE videometadata SET filename='$qrel_filename',host='$hostname'  WHERE intid = $db_data->[0]->[13]";
				if(!$dryrun)
				{
					$dbh = MythTV::open_connection();
					$dbh->do($query);
					MythTV::close_connection();
				}
				else
				{
					print "DB Query: ".$query."\n";
				}
			}
			if($migrated)
			{
				# Update the database here
				print "Migrating metadata to Mythtv 0.22 and higher\n";
				UpdateDataBase($filename, \%result);
			}
		}
	}
	else
	{
		my $qFilename = $filename;
		$qFilename =~ s/\'/\\\'/g;
		my $db_data = MythTV::query("SELECT title,director,plot,year,length,coverfile FROM videometadata WHERE filename LIKE '$qFilename'");
		if(@$db_data)
		{
			$result{Title} = $db_data->[0]->[0];
			$result{Director} = $db_data->[0]->[1];
			$result{Plot} = $db_data->[0]->[2];
			$result{Year} = $db_data->[0]->[3];
			if($db_data->[0]->[4] > 0)
			{
				$result{Length} = $db_data->[0]->[4];
			}
			$result{PosterFile} = $db_data->[0]->[5];
			if($result{Title} =~ /^(.*?)\s+S(\d\d?)E(\d\d?)\s*(.*?)$/i)
			{
				$result{Title} = $1;
				$result{Season} = $2;
				$result{Episode} = $3;
				$result{Subtitle} = $4;
			}
		}
	}
	$result{'filename'} = $filename;
	return %result;
}


sub getImage
{
	my $url = shift;
	my $file = shift;
	my $response;
	my $result=-1;
	
	print "Downloading image\n";
	print "   from: ".$url."\n"; 
	print "     to: ".$file."\n";
	# Get image if:
	# a. It doesn't exist
	# b. It is older than 60 days (perhaps there is new art work)
	# c. Update is enabled, and is older than 1 day
	if(!(-s"$file") || (-M"$file" > 60) || (($update==1) && (-M"$file" > 1)))
	{
		if(!$dryrun)
		{
			$response = getstore($url,$file);
			print "  Server Response: $response\n";
			if($response == 200)
			{
				$result = 0;
			}
			else
			{
				$result = -1;
				print "  FAILED to get image.\n";
			}
		}
		else
		{
			$result = 2;
			print "  Dryrun enabled, won't download\n";
		}
	}
	else
	{
		$result = 1;
		print "  Image exists, won't overwrite.\n";
	}
	return $result;
}

sub addPerson
{
	my $person=shift;

	# does person exist in videocast
	$db_data = MythTV::query("select intid from videocast where cast like '$person'");
	if(@$db_data == 0)
	{
		# add person
#		print " Adding: $person\r\n";
		$dbh = MythTV::open_connection();
		$dbh->do("insert into videocast (cast) values ('$person')");
		MythTV::close_connection();
	}
	else
	{
#		print "Person already in DB\r\n";
	}
}

sub linkPersonToTitle
{
	my $person=shift;
	my $filename=shift;
	
	my $personid;
	my $videoid;


	addPerson($person);
	$db_data = MythTV::query("select intid from videocast where cast like '$person'");
	if(@$db_data > 0)
	{
		$personid = $db_data->[0]->[0];
		$db_data = MythTV::query("select intid from videometadata where filename = '$filename'");
		if(@$db_data > 0)
		{
			$videoid = $db_data->[0]->[0];
			# add link
#			print "Linking person:$personid to video:$videoid\r\n";
			$dbh = MythTV::open_connection();
			$dbh->do("insert into videometadatacast (idvideo,idcast) values ('$videoid','$personid')");
			MythTV::close_connection();
		}
		else
		{
#			print "Video Not found\r\n";
		}
	}
	else
	{
#		print "Person not found\r\n";
	}
}

sub updateCast
{
	my $filename=shift;
	my $castlist=shift;

	my @cast;

	@cast = split(/\,/, $castlist);
	foreach my $person (@cast)
	{
#		print "Procesing: $person\r\n";
		# check to see if cast is alread linked to video
		#delimit parameters
		$person =~ s/\'/\\\'/g;
		$db_data = MythTV::query("select videocast.intid from videocast left join videometadatacast on videocast.intid=videometadatacast.idcast left join videometadata on videometadatacast.idvideo=videometadata.intid where videometadata.filename = '$filename' and videocast.cast = '$person'");
		if(@$db_data == 0)
		{
			linkPersonToTitle($person, $filename);
		}
		else
		{
#			print "Person already linked\r\n";
		}
	}	
}

sub UpdateDataBase
{
	my $filename = shift;
	my $ref = shift;
	my %details = %$ref;

	my $query;

	$filename = getGroupSubpath($filename,\@video_group);
	if( $filename !~ /^\// )
	{
		$details{PosterFile} = getGroupSubpath($details{PosterFile},\@cover_group);
		$details{ScreenshotFile} = getGroupSubpath($details{ScreenshotFile},\@screenshot_group);
		$details{FanartFile} = getGroupSubpath($details{FanartFile},\@fanart_group);
		$details{BannerFile} = getGroupSubpath($details{BannerFile},\@banner_group);
	}
	if( $db_schema < 1028 )
	{
		#Prepare the title
		if(length $details{Season} >0){
			if(length $details{Episode} >0)	{
				if(length $details{Subtitle} >0){
					$ShowName = sprintf("%s S%02dE%02d %s",$details{Title}, $details{Season}, $details{Episode}, $details{Subtitle});
				}
				else{
					$ShowName = sprintf("%s S%02dE%02d",$details{Title}, $details{Season}, $details{Episode});
				}
			}
			else{
				$ShowName = sprintf("%s Season %d", $details{Title}, $details{Season} );
			}
		}
		elsif(length $details{Episode} >0){
			$ShowName = sprintf("%s Part %d", $details{Title}, $details{Episode} );
		}
		else{
			$ShowName = $details{Title};
		}
	}
	#Delimit single quotes
	$ShowName =~ s/\'/\\\'/g;
	$details{Title} =~ s/\'/\\\'/g;
	$details{Subtitle} =~ s/\'/\\\'/g;
	$details{Plot} = clean_nonutf8($details{Plot});
	$details{Plot} =~ s/\'/\\\'/g;
	$filename =~ s/\'/\\\'/g;
	$details{PosterFile} =~ s/\'/\\\'/g;
	$details{ScreenshotFile} =~ s/\'/\\\'/g;
	$details{FanartFile} =~ s/\'/\\\'/g;
	$details{BannerFile} =~ s/\'/\\\'/g;
	$details{Director} =~ s/\'/\\\'/g;
	$pl = GetParentalLevel($details{Rating});
	my $title = MythTV::query("SELECT title FROM videometadata WHERE filename LIKE '$filename'");
	if (@$title > 0)
	{
		#entry already exist, so update it.
		if( $db_schema >= 1028 )
		{
			$query = "UPDATE videometadata SET title = '$details{Title}', subtitle = '$details{Subtitle}', season = '$details{Season}', episode = '$details{Episode}', plot = '$details{Plot}',director = '$details{Director}',year = '$details{Year}',length = '$details{Length}',rating = '$details{Rating}',showlevel = '$pl'";
			if($filename !~ /^\//)
			{
				$query = $query.",host = '$hostname'";
			}
		}
		else
		{
			$query = "UPDATE videometadata SET title = '$ShowName', plot = '$details{Plot}',director = '$details{Director}',year = '$details{Year}',length = '$details{Length}',rating = '$details{Rating}',showlevel = '$pl'";
		}
		if(length $details{PosterFile} > 0)
		{
			$query = $query.", coverfile = '$details{PosterFile}'";
		}
		if( $db_schema >= 1028 )
		{
			if(length $details{ScreenshotFile} > 0)	{
				$query = $query.", screenshot = '$details{ScreenshotFile}'";
			}
			if(length $details{BannerFile} > 0)	{
				$query = $query.", banner = '$details{BannerFile}'";
			}
			if(length $details{FanartFile} > 0)	{
				$query = $query.", fanart = '$details{FanartFile}'";
			}
		}
		$query = $query." WHERE filename = '$filename'";
		if(!$dryrun)
		{
			print "Updating DB entry\n";
			$dbh = MythTV::open_connection();
			$dbh->do($query);
			MythTV::close_connection();
		}
		else
		{
			print "DB Query: ".$query."\n";
		}
	}
	else
	{
		if( $db_schema >= 1028 )
		{
			if($filename !~ /^\//)
			{
				$query = "INSERT INTO videometadata (filename,host,title,subtitle,season,episode,plot,director,coverfile,screenshot,banner,fanart,year,length,rating,showlevel) values ('$filename','$hostname','$details{Title}','$details{Subtitle}','$details{Season}','$details{Episode}','$details{Plot}','$details{Director}','$details{PosterFile}','$details{ScreenshotFile}','$details{BannerFile}','$details{FanartFile}','$details{Year}','$details{Length}','$details{Rating}','$pl')";
			}
			else
			{
				$query = "INSERT INTO videometadata (filename,title,subtitle,season,episode,plot,director,coverfile,screenshot,banner,fanart,year,length,rating,showlevel) values ('$filename','$details{Title}','$details{Subtitle}','$details{Season}','$details{Episode}','$details{Plot}','$details{Director}','$details{PosterFile}','$details{ScreenshotFile}','$details{BannerFile}','$details{FanartFile}','$details{Year}','$details{Length}','$details{Rating}','$pl')";
			}
		}
		else
		{
			$query = "INSERT INTO videometadata (filename,title,plot,director,coverfile,year,length,rating) values ('$filename','$ShowName','$details{Plot}','$details{Director}','$details{PosterFile}','$details{Year}','$details{Length}','$details{Rating}')";
		}
		if(!$dryrun)
		{
			print "Adding DB entry\n";
			$dbh = MythTV::open_connection();
			$dbh->do($query);
			MythTV::close_connection();
		}
		else
		{
			print "DB Query: ".$query."\n";
		}
	}
	if(!$dryrun)
	{
		updateCast($filename, $details{Cast});
	}	
}
